using System;
using System.IO;
// Zur Arbeit mit Datei
using System.Collections.Generic;
// Grundlegende Datenstrukturen
namespace GraphSpace
{
	public class DiGraph<TV, TE> : Graph<TV, TE> where TV : Knoten, new() where TE : Kante, new()
	{
		private Dictionary<int, List<TE>> a;
		private Dictionary<int, List<TE>> ar;

		public DiGraph ()
		{
			m = 0;
			v = new List<TV> ();
			a = new Dictionary<int, List<TE>> ();
			ar = new Dictionary<int, List<TE>> ();
		}

		/// <summary>Methode zum Einfuegen eines neuen Knotens</summary>
		/// <returns>false, wenn Knoten bereits im Graphen vorhanden ist</returns>
		/// <param name="id">ID des einzufügenden Knotens</param>
		/// <param name="daten">Daten am Knoten</param>
		public override bool neuerKnoten (int id)
		{
			TV knoten = new TV ();
			knoten.init (id);
			// Pruefe, ob Knoten bereits vorhanden ist
			if (!enthaeltKnoten (knoten.Id)) {
				v.Add (knoten);
				// Knoten aufnehmen
				a.Add (knoten.Id, new List<TE> ());
				ar.Add (knoten.Id, new List<TE> ());
				// Adjazensliste fuer Knoten anlegen (ungerichtet)
			}
			
			// Knoten bereits vorhanden, wurde nicht eingefuegt
			return false;
		}
		
		public override bool neuerKnoten (TV knoten)
		{
			// Pruefe, ob Knoten bereits vorhanden ist
			if (!enthaeltKnoten (knoten.Id)) {
				v.Add (knoten);
				// Knoten aufnehmen
				a.Add (knoten.Id, new List<TE> ());
				ar.Add (knoten.Id, new List<TE> ());
				// Adjazensliste fuer Knoten anlegen (ungerichtet)
			}
			
			// Knoten bereits vorhanden, wurde nicht eingefuegt
			return false;
		}

		/// <summary>Methode zum EInfuegen einer neuen Kante (auch parallel).</summary>
		/// <param name="hin">einzufuegende Kante von s nacht t</param>
		/// <param name="rueck">einzufuegende Kante von t nacht s</param>
		/// <returns>false, wenn einer der beiden Knoten nicht im Graphen vorhanden</returns>	
		public override bool neueKante (int von, int nach, double gewicht)
		{
			TV knoten1 = holeKnoten (von);
			TV knoten2 = holeKnoten (nach);
			
			TE hin = new TE ();
			hin.init (knoten1, knoten2, gewicht);
			TE rueck = new TE ();
			rueck.init (knoten2, knoten1, gewicht);
			
			
			if (enthaeltKnoten (von) && enthaeltKnoten (nach)) {
				// ungerichtet
				a[von].Add (hin);
				// Kante in Adjazensliste des Start-Knotens einfuegen
				ar[nach].Add (rueck);
				// Kante in Adjazensliste des Start-Knotens einfuegen (2. Richtung)
				// gerichtet
				m++;
				return true;
			}
			
			return false;
			// Knoten war noch nicht vorhanden
		}

		public override bool neueKante (TE hin, TE rueck)
		{
			
			if (enthaeltKnoten (hin.Von) && enthaeltKnoten (hin.Nach)) {
				// ungerichtet
				a[hin.Von].Add (hin);
				// Kante in Adjazensliste des Start-Knotens einfuegen
				ar[hin.Nach].Add (rueck);
				// Kante in Adjazensliste des Start-Knotens einfuegen (2. Richtung)
				// gerichtet
				m++;
				return true;
			}
			
			return false;
			// Knoten war noch nicht vorhanden
		}
		
		/// <summary>Methode zum Einlesen eines Graphen aus Datei. 
		/// Dabei werden alle zuvor gesetzten Knoten und Kanten entfernt</summary>
		/// <param name="pfad">Pfad zur Datei</param>
		/// <returns>false, wenn Datei nicht eingelesen werden konnte</returns>
		public override bool ladeGraph (string pfad)
		{
			string zeile = "";
			if (File.Exists (pfad)) {
				// Graph leeren				
				v = new List<TV> ();
				a = new Dictionary<int, List<TE>> ();
				ar = new Dictionary<int, List<TE>> ();
				
				// Datei einlesen
				StreamReader reader = new StreamReader (pfad, System.Text.Encoding.Default);
				zeile = reader.ReadLine ();
				// erste Zeile mit Basiswerten einlesen und verwerfen
				while (!reader.EndOfStream) {
					zeile = reader.ReadLine ();
					if (zeile.Split (' ').GetLength (0) > 1) {
						// Startknoten der Kante
						int von = Convert.ToInt32 (zeile.Split (' ')[1]);
						neuerKnoten (von);
						// Zielknoten der Kante
						int nach = Convert.ToInt32 (zeile.Split (' ')[2]);
						neuerKnoten (nach);
						neueKante (von, nach, 0.0);
						// Kante einfuegen
					}
				}
				
				reader.Close ();
				//Console.WriteLine("Graph geladen");
				return true;
			}
			
			return false;
		}

		public override bool speicherGraph (string pfad)
		{
			try {
				// Datei einlesen
				StreamWriter writer = new StreamWriter (pfad);
				writer.WriteLine ("n " + v.Count + " m " + m);
				
				foreach (TV w in v)
					foreach (TE k in a[w.Id])
						writer.WriteLine ("e " + k.Von + " " + k.Nach);
				
				writer.Close ();
				Console.WriteLine ("Graph gespeichert");
				return true;
			} catch {
				return false;
			}
		}

		public Dictionary<int, List<TE>> A {
			get { return this.a; }
		}

		public Dictionary<int, List<TE>> AR {
			get { return this.ar; }
		}
	}
}

